package com.fw4j.jdbc.engine;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fw4j.jdbc.annotation.Column;
import com.fw4j.jdbc.annotation.Entity;
import com.fw4j.jdbc.datasource.pool.DataSources;
import com.fw4j.jdbc.exception.InstanceException;
import com.fw4j.jdbc.factory.EntityFactory;


/**
 * @author 穆仁超
 * 数据库操作类
 * 日期 2013-12-27 16:34
 * 我的QQ群：32894018
 */
public class Operate {

	private final Logger logger = LoggerFactory.getLogger(Operate.class);
	private java.sql.Connection connect = null;
	private ResultSet resultSet = null;
	
	public Operate() throws SQLException{
		this(DataSources.getInstance().getConnection());
	}
	public Operate(java.sql.Connection conn){
		if(conn == null){
			this.connect = DataSources.getInstance().getConnection();
		}else{
			this.connect = conn;
		}
	}
	private <T> List<T> result(Class<T> entity,ResultSet res) throws InstanceException, SQLException{
		try{
		if(entity.getAnnotation(Entity.class)!=null){
			if(res != null){
				List<Object> objList = new ArrayList<Object>();
				while(res.next()){
					Object obj = null;
					obj = EntityFactory.getInstance(entity);
					Method[] ms = entity.getMethods();
					for(Method m : ms){
						Column pt = m.getAnnotation(Column.class);
						this.setValueToEntity(obj,m, pt, res);
					}//end if
					objList.add(obj);
				}//end while
				return (List<T>)objList;
			}//end if
		}//end if
		}catch (SecurityException e) {
			e.printStackTrace();
		} 
		return null;
	}
	private void setValueToEntity(Object obj,Method method,Column pt,ResultSet res) throws SQLException{
		try {
			if(pt != null){
				String column = pt.value().toLowerCase();
				StringBuffer methodName = new StringBuffer(method.getName());
				String setMethod = methodName.toString();
				if(!pt.type().equals("")){
					String type = pt.type();
					if(type.equals(Column.STRING)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getString(column));
					 		return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getString(column));
							return;
						}//end switch
					}else if(type.equals(Column.INTEGER)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getInt(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getInt(column));
							return;
						}//end switch
					}else if(type.equals(Column.DATE)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getDate(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getDate(column));
							return;
						}//end switch
					}else if(type.equals(Column.LONG)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getLong(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getLong(column));
							return;
						}//end switch
					}else if(type.equals(Column.SHORT)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getShort(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getShort(column));
							return;
						}//end switch
					}else if(type.equals(Column.FLOAT)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getFloat(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getFloat(column));
							return;
						}//end switch
					}else if(type.equals(Column.DOUBLE)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getDouble(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getDouble(column));
							return;
						}//end switch
					}else if(type.equals(Column.BYTE)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getByte(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getByte(column));
							return;
						}//end switch
					}else if(type.equals(Column.BOOLEAN)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getBoolean(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getBoolean(column));
							return;
						}//end switch
					}else if(type.equals(Column.BIGDECIMAL)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getBigDecimal(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getBigDecimal(column));
							return;
						}//end switch
					}else if(type.equals(Column.BLOB)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getBlob(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getBlob(column));
							return;
						}//end switch
					}else if(type.equals(Column.CLOB)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getClob(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getClob(column));
							return;
						}//end switch
					}else if(type.equals(Column.ARRAY)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getArray(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getArray(column));
							return;
						}//end switch
					}else if(type.equals(Column.ASCIISTREAM)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getAsciiStream(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getAsciiStream(column));
							return;
						}//end switch
					}else if(type.equals(Column.BINARYSTREAM)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getBinaryStream(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getBinaryStream(column));
							return;
						}//end switch
					}else if(type.equals(Column.CHARACTERSTREAM)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getCharacterStream(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getCharacterStream(column));
							return;
						}//end switch
					}else if(type.equals(Column.BYTES)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getBytes(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getBytes(column));
							return;
						}//end switch
					}else if(type.equals(Column.NCHARACTERSTREAM)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getNCharacterStream(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getNCharacterStream(column));
							return;
						}//end switch
					}else if(type.equals(Column.NCLOB)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getNClob(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getNClob(column));
							return;
						}//end switch
					}else if(type.equals(Column.NSTRING)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getNString(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getNString(column));
							return;
						}//end switch
					}else if(type.equals(Column.OBJECT)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getObject(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getObject(column));
							return;
						}//end switch
					}else if(type.equals(Column.TIME)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getTime(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getTime(column));
							return;
						}//end switch
					}else if(type.equals(Column.TIMESTAMP)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getTimestamp(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getTimestamp(column));
							return;
						}//end switch
					}else if(type.equals(Column.SQLXML)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getSQLXML(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getSQLXML(column));
							return;
						}//end switch
					}else if(type.equals(Column.ROWID)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getRowId(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "get").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getRowId(column));
							return;
						}//end switch
					}else if(type.equals(Column.REF)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getRef(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "get").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getRef(column));
							return;
						}//end switch
					}else if(type.equals(Column.URL)){
						switch(setMethod.charAt(0)){
						case 's':
							obj.getClass().getMethod(setMethod, method.getParameterTypes()).invoke(obj, res.getURL(column));
							return;
						case 'g':
							setMethod = methodName.replace(0, 3, "set").toString();
							obj.getClass().getMethod(setMethod, method.getReturnType()).invoke(obj, res.getURL(column));
							return;
						}//end switch
					}//end if
				}
			}//end if
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}
	public <T> List<T> query(Class<T> entity, String sql,Object[] param) throws SQLException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
		try {
				PreparedStatement pstmt = this.connect.prepareStatement(sql);
				if(param != null){
					for(int i=0;i<param.length;i++){
							pstmt.setObject(i+1, param[i]);
					}//end for
				}//end if
				String s = pstmt.toString();
				logger.warn("执行"+s.substring(s.indexOf(":")+1));
				resultSet = pstmt.executeQuery();
				List<T> resList;
				resList = this.result(entity, resultSet);
				resultSet.close();
				pstmt.close();
				if(this.connect.getAutoCommit()){
					this.connect.close();
				}//end if
			return (List<T>)resList;
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InstanceException e) {
				e.printStackTrace();
			}
		return null;
	}
	
	public int modify(String sql,Object[] param) throws SQLException{
			PreparedStatement pstmt = this.connect.prepareStatement(sql);
			if(param != null){
				for(int i=0;i<param.length;i++){
					try {
						pstmt.setObject(i+1, param[i]);
					} catch (SQLException e) {			
						e.printStackTrace();
					}
				}
			}
			logger.info("执行："+pstmt.toString());
			int c = pstmt.executeUpdate();
			pstmt.close();
			if(this.connect.getAutoCommit()){
				this.connect.close();
			}//end if
			return c;
	}
	
}
